﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Runtime.InteropServices;
using System.Drawing;
using Apparat.ShaderManagement;

namespace Apparat.Renderables
{
  public class Torus : Renderable
  {
    SlimDX.Direct3D11.Buffer vertexBuffer;
    SlimDX.Direct3D11.Buffer indexBuffer;
    DataStream vertices;
    DataStream indices;

    InputLayout layout;

    int numVertices = 0;
    int numIndices = 0;

    int vertexStride = 0;

    public Torus(float radius, float ringRadius, int sides, int rings)
    {


      int numVerticesPerRow = sides + 1;
      int numVerticesPerColumn = rings + 1;

      numVertices = numVerticesPerRow * numVerticesPerColumn;

      vertexStride = Marshal.SizeOf(typeof(Vector3)); // 12 bytes
      int SizeOfVertexBufferInBytes = numVertices * vertexStride;

      vertices = new DataStream(SizeOfVertexBufferInBytes, true, true);

      float theta = 0.0f;
      float phi = 0.0f;

      float verticalAngularStride = (float)(Math.PI * 2.0f) / (float)rings;
      float horizontalAngularStride = ((float)Math.PI * 2.0f) / (float)sides;

      for (int verticalIt = 0; verticalIt < numVerticesPerColumn; verticalIt++)
      {
        theta = verticalAngularStride * verticalIt;

        for (int horizontalIt = 0; horizontalIt < numVerticesPerRow; horizontalIt++)
        {
          phi = horizontalAngularStride * horizontalIt;

          // position
          float x = (float)Math.Cos(theta) * (radius + ringRadius * (float)Math.Cos(phi));
          float y = (float)Math.Sin(theta) * (radius + ringRadius * (float)Math.Cos(phi));
          float z = ringRadius * (float)Math.Sin(phi);

          Vector3 position = new Vector3(x, z, y);
          vertices.Write(position);
        }
      }

      vertices.Position = 0;

      // create the vertex buffer
      vertexBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device,
        vertices,
        SizeOfVertexBufferInBytes,
        ResourceUsage.Default,
        BindFlags.VertexBuffer,
        CpuAccessFlags.None,
        ResourceOptionFlags.None,
        0);

      numIndices = sides * rings * 6;
      indices = new DataStream(2 * numIndices, true, true);

      for (int verticalIt = 0; verticalIt < rings; verticalIt++)
      {
        for (int horizontalIt = 0; horizontalIt < sides; horizontalIt++)
        {
          short lt = (short)(horizontalIt + verticalIt * (numVerticesPerRow));
          short rt = (short)((horizontalIt + 1) + verticalIt * (numVerticesPerRow));

          short lb = (short)(horizontalIt + (verticalIt + 1) * (numVerticesPerRow));
          short rb = (short)((horizontalIt + 1) + (verticalIt + 1) * (numVerticesPerRow));

          indices.Write(lt);
          indices.Write(rt);
          indices.Write(lb);

          indices.Write(rt);
          indices.Write(rb);
          indices.Write(lb);
        }
      }

      indices.Position = 0;

      indexBuffer = new SlimDX.Direct3D11.Buffer(
          DeviceManager.Instance.device,
          indices,
          2 * numIndices,
          ResourceUsage.Default,
          BindFlags.IndexBuffer,
          CpuAccessFlags.None,
          ResourceOptionFlags.None,
          0);


    }

    EffectWrapperTransformEffectWireframe ew = ShaderManager.Instance.transformEffectWireFrame;

    public override void Render()
    {
      Matrix ViewPerspective = CameraManager.Instance.ViewPerspective;
      Matrix WorldViewPerspective = this.transform * ViewPerspective;



      ew.tmat.SetMatrix(WorldViewPerspective);

      ew.mCol.Set(new Color4(1, 0, 1, 0));
      ew.wfCol.Set(new Color4(1, 0, 0, 0));

      // configure the Input Assembler portion of the pipeline with the vertex data
      DeviceManager.Instance.context.InputAssembler.InputLayout = layout;
      DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;
      DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexStride, 0));
      DeviceManager.Instance.context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0);

      ew.technique = ew.effect.GetTechniqueByName("Render");

      EffectTechniqueDescription techDesc;
      techDesc = ew.technique.Description;

      for (int p = 0; p < techDesc.PassCount; ++p)
      {
        ew.technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
        DeviceManager.Instance.context.DrawIndexed(numIndices, 0, 0);
      }
    }

    public override void Dispose()
    {

    }

    public override Matrix Transform
    {
      get
      {
        return this.transform;
      }
      set
      {
        this.transform = value;
      }
    }
  }
}
